package cn.assumejson.springframework.beans.factory.support;

import cn.assumejson.springframework.beans.factory.BeansException;
import cn.assumejson.springframework.beans.factory.config.BeanDefinition;
import cn.assumejson.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import cn.assumejson.springframework.context.context.annotation.Configuration;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 操作Bean 的核心方法
 *
 * @Author Mr. Li
 * @Date 2021/11/29 23:26
 * @Version 1.0
 */
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry, ConfigurableListableBeanFactory, Serializable {
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();


    @Override
    public boolean containsBean(String name) {
        return beanDefinitionMap.containsKey(name);
    }

    @Override
    public BeanDefinition getBeanDefinition(String name) {
        BeanDefinition bd = beanDefinitionMap.get(name);
        if (bd == null) throw new BeansException("No bean named '" + name + "' is defined");
        return bd;
    }

    @Override
    public RootBeanDefinition registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        // 判断当前BeanDefinition是否存在BeanFactory
        beanDefinitionMap.put(beanName, beanDefinition);
        return (RootBeanDefinition) beanDefinition;
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        return ObjectUtil.isNotNull(beanDefinition);
    }

    /**
     * 根据type 在beanDefinitionMap中筛选出符合类型的对象
     *
     * @param type
     * @param <T>
     * @return
     * @throws BeansException
     */
    @Override
    @SuppressWarnings("unchecked")
    public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException {
        Map<String, T> result = new HashMap<>();
        beanDefinitionMap.forEach((s, bd) -> {
            Class<?> beanClass = bd.getBeanClass();
            // isAssignableFrom 是一个父亲的在前面，子类在后面
            // 跳过所有的配置类
            if (!shouldSkip(bd)) {
                if (type.isAssignableFrom(beanClass)) {
                    result.put(s, (T) getBean(s));
                }
            }
        });
        return result;
    }

    @Override
    public String[] getBeanDefinitionNames() {
//        List<String> collect = beanDefinitionMap.keySet().stream().collect(Collectors.toList());
//        return collect.toArray(new String[0]);

//        return beanDefinitionMap.keySet().stream().toArray(String[]::new);
        return beanDefinitionMap.keySet().toArray(new String[0]);
    }

    @Override
    public void preInstantiateSingletons() throws BeansException {
        // 实例化所有的单例Bean,跳过配置类
        beanDefinitionMap.forEach((beanName, beanDefinition) -> {
            if (!shouldSkip(beanDefinition)) {
                getBean(beanName);
            }
        });
//        beanDefinitionMap.keySet().forEach(this::getBean);
    }

    private boolean shouldSkip(BeanDefinition beanDefinition) {
        Configuration annotation = beanDefinition.getBeanClass().getAnnotation(Configuration.class);
        return ObjectUtil.isNotNull(annotation);
    }

    @Override
    public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
        if (includeNonSingletons && allowEagerInit) {
            return doGetBeanNamesForType(type);
        }
        return doGetBeanNamesForType(type);
    }

    private String[] doGetBeanNamesForType(Class<?> type) {
        // 获取所有的BeanName
        String[] beanDefinitionNames = this.getBeanDefinitionNames();
        if (ArrayUtil.isNotEmpty(beanDefinitionNames)) {
            return beanDefinitionNames;
        }
        return new String[0];
    }
}
